// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_UTIL_H_
#define V8_UTIL_H_

#include "v8.h" // NOLINT(build/include)
#include <assert.h>
#include <map>
#include <vector>

/**
 * Support for Persistent containers.
 *
 * C++11 embedders can use STL containers with Global values,
 * but pre-C++11 does not support the required move semantic and hence
 * may want these container classes.
 */
namespace v8 {

typedef uintptr_t PersistentContainerValue;
static const uintptr_t kPersistentContainerNotFound = 0;
enum PersistentContainerCallbackType {
    kNotWeak,
    // These correspond to v8::WeakCallbackType
    kWeakWithParameter,
    kWeakWithInternalFields
};

/**
 * A default trait implementation for PersistentValueMap which uses std::map
 * as a backing map.
 *
 * Users will have to implement their own weak callbacks & dispose traits.
 */
template <typename K, typename V>
class StdMapTraits {
public:
    // STL map & related:
    typedef std::map<K, PersistentContainerValue> Impl;
    typedef typename Impl::iterator Iterator;

    static bool Empty(Impl* impl) { return impl->empty(); }
    static size_t Size(Impl* impl) { return impl->size(); }
    static void Swap(Impl& a, Impl& b) { std::swap(a, b); } // NOLINT
    static Iterator Begin(Impl* impl) { return impl->begin(); }
    static Iterator End(Impl* impl) { return impl->end(); }
    static K Key(Iterator it) { return it->first; }
    static PersistentContainerValue Value(Iterator it) { return it->second; }
    static PersistentContainerValue Set(Impl* impl, K key,
        PersistentContainerValue value)
    {
        std::pair<Iterator, bool> res = impl->insert(std::make_pair(key, value));
        PersistentContainerValue old_value = kPersistentContainerNotFound;
        if (!res.second) {
            old_value = res.first->second;
            res.first->second = value;
        }
        return old_value;
    }
    static PersistentContainerValue Get(Impl* impl, K key)
    {
        Iterator it = impl->find(key);
        if (it == impl->end())
            return kPersistentContainerNotFound;
        return it->second;
    }
    static PersistentContainerValue Remove(Impl* impl, K key)
    {
        Iterator it = impl->find(key);
        if (it == impl->end())
            return kPersistentContainerNotFound;
        PersistentContainerValue value = it->second;
        impl->erase(it);
        return value;
    }
};

/**
 * A default trait implementation for PersistentValueMap, which inherits
 * a std:map backing map from StdMapTraits and holds non-weak persistent
 * objects and has no special Dispose handling.
 *
 * You should not derive from this class, since MapType depends on the
 * surrounding class, and hence a subclass cannot simply inherit the methods.
 */
template <typename K, typename V>
class DefaultPersistentValueMapTraits : public StdMapTraits<K, V> {
public:
    // Weak callback & friends:
    static const PersistentContainerCallbackType kCallbackType = kNotWeak;
    typedef PersistentValueMap<K, V, DefaultPersistentValueMapTraits<K, V>>
        MapType;
    typedef void WeakCallbackDataType;

    static WeakCallbackDataType* WeakCallbackParameter(
        MapType* map, const K& key, Local<V> value)
    {
        return nullptr;
    }
    static MapType* MapFromWeakCallbackInfo(
        const WeakCallbackInfo<WeakCallbackDataType>& data)
    {
        return nullptr;
    }
    static K KeyFromWeakCallbackInfo(
        const WeakCallbackInfo<WeakCallbackDataType>& data)
    {
        return K();
    }
    static void DisposeCallbackData(WeakCallbackDataType* data) { }
    static void Dispose(Isolate* isolate, Global<V> value, K key) { }
};

template <typename K, typename V>
class DefaultGlobalMapTraits : public StdMapTraits<K, V> {
private:
    template <typename T>
    struct RemovePointer;

public:
    // Weak callback & friends:
    static const PersistentContainerCallbackType kCallbackType = kNotWeak;
    typedef GlobalValueMap<K, V, DefaultGlobalMapTraits<K, V>> MapType;
    typedef void WeakCallbackDataType;

    static WeakCallbackDataType* WeakCallbackParameter(MapType* map, const K& key,
        Local<V> value)
    {
        return nullptr;
    }
    static MapType* MapFromWeakCallbackInfo(
        const WeakCallbackInfo<WeakCallbackDataType>& data)
    {
        return nullptr;
    }
    static K KeyFromWeakCallbackInfo(
        const WeakCallbackInfo<WeakCallbackDataType>& data)
    {
        return K();
    }
    static void DisposeCallbackData(WeakCallbackDataType* data) { }
    static void OnWeakCallback(
        const WeakCallbackInfo<WeakCallbackDataType>& data) { }
    static void Dispose(Isolate* isolate, Global<V> value, K key) { }
    // This is a second pass callback, so SetSecondPassCallback cannot be called.
    static void DisposeWeak(const WeakCallbackInfo<WeakCallbackDataType>& data) { }

private:
    template <typename T>
    struct RemovePointer<T*> {
        typedef T Type;
    };
};

/**
 * A map wrapper that allows using Global as a mapped value.
 * C++11 embedders don't need this class, as they can use Global
 * directly in std containers.
 *
 * The map relies on a backing map, whose type and accessors are described
 * by the Traits class. The backing map will handle values of type
 * PersistentContainerValue, with all conversion into and out of V8
 * handles being transparently handled by this class.
 */
template <typename K, typename V, typename Traits>
class PersistentValueMapBase {
public:
    Isolate* GetIsolate() { return isolate_; }

    /**
   * Return size of the map.
   */
    size_t Size() { return Traits::Size(&impl_); }

    /**
   * Return whether the map holds weak persistents.
   */
    bool IsWeak() { return Traits::kCallbackType != kNotWeak; }

    void SetReference(const K& key, const Persistent<Object>& parent) { } // weolar

    /**
   * Get value stored in map.
   */
    Local<V> Get(const K& key)
    {
        return Local<V>::New(isolate_, FromVal(Traits::Get(&impl_, key)));
    }

    /**
   * Check whether a value is contained in the map.
   */
    bool Contains(const K& key)
    {
        return Traits::Get(&impl_, key) != kPersistentContainerNotFound;
    }

    /**
   * Get value stored in map and set it in returnValue.
   * Return true if a value was found.
   */
    bool SetReturnValue(const K& key,
        ReturnValue<Value> returnValue)
    {
        return SetReturnValueFromVal(&returnValue, Traits::Get(&impl_, key));
    }

    /**
   * Call V8::RegisterExternallyReferencedObject with the map value for given
   * key.
   */
    V8_DEPRECATED(
        "Used TracedGlobal and EmbedderHeapTracer::RegisterEmbedderReference",
        inline void RegisterExternallyReferencedObject(K& key));

    /**
   * Return value for key and remove it from the map.
   */
    Global<V> Remove(const K& key)
    {
        return Release(Traits::Remove(&impl_, key)).Pass();
    }

    /**
  * Traverses the map repeatedly,
  * in case side effects of disposal cause insertions.
  **/
    void Clear()
    {
        typedef typename Traits::Iterator It;
        HandleScope handle_scope(isolate_);
        // TODO(dcarney): figure out if this swap and loop is necessary.
        while (!Traits::Empty(&impl_)) {
            typename Traits::Impl impl;
            Traits::Swap(impl_, impl);
            for (It i = Traits::Begin(&impl); i != Traits::End(&impl); ++i) {
                Traits::Dispose(isolate_, Release(Traits::Value(i)).Pass(),
                    Traits::Key(i));
            }
        }
    }

    /**
   * Helper class for GetReference/SetWithReference. Do not use outside
   * that context.
   */
    class PersistentValueReference {
    public:
        PersistentValueReference()
            : value_(kPersistentContainerNotFound)
        {
        }
        PersistentValueReference(const PersistentValueReference& other)
            : value_(other.value_)
        {
        }

        Local<V> NewLocal(Isolate* isolate) const
        {
            return Local<V>::New(isolate, FromVal(value_));
        }
        bool IsEmpty() const
        {
            return value_ == kPersistentContainerNotFound;
        }
        template <typename T>
        bool SetReturnValue(ReturnValue<T> returnValue)
        {
            return SetReturnValueFromVal(&returnValue, value_);
        }
        void Reset()
        {
            value_ = kPersistentContainerNotFound;
        }
        void operator=(const PersistentValueReference& other)
        {
            value_ = other.value_;
        }

    private:
        friend class PersistentValueMapBase;
        friend class PersistentValueMap<K, V, Traits>;
        friend class GlobalValueMap<K, V, Traits>;

        explicit PersistentValueReference(PersistentContainerValue value)
            : value_(value)
        {
        }

        void operator=(PersistentContainerValue value)
        {
            value_ = value;
        }

        PersistentContainerValue value_;
    };

    /**
   * Get a reference to a map value. This enables fast, repeated access
   * to a value stored in the map while the map remains unchanged.
   *
   * Careful: This is potentially unsafe, so please use with care.
   * The value will become invalid if the value for this key changes
   * in the underlying map, as a result of Set or Remove for the same
   * key; as a result of the weak callback for the same key; or as a
   * result of calling Clear() or destruction of the map.
   */
    PersistentValueReference GetReference(const K& key)
    {
        return PersistentValueReference(Traits::Get(&impl_, key));
    }

protected:
    explicit PersistentValueMapBase(Isolate* isolate)
        : isolate_(isolate)
        , label_(nullptr)
    {
    }
    PersistentValueMapBase(Isolate* isolate, const char* label)
        : isolate_(isolate)
        , label_(label)
    {
    }

    ~PersistentValueMapBase() { Clear(); }

    Isolate* isolate() { return isolate_; }
    typename Traits::Impl* impl() { return &impl_; }

    static V* FromVal(PersistentContainerValue v)
    {
        return reinterpret_cast<V*>(v);
    }

    static PersistentContainerValue ClearAndLeak(Global<V>* persistent)
    {
        V* v = persistent->val_;
        persistent->val_ = nullptr;
        return reinterpret_cast<PersistentContainerValue>(v);
    }

    static PersistentContainerValue Leak(Global<V>* persistent)
    {
        return reinterpret_cast<PersistentContainerValue>(persistent->val_);
    }

    /**
   * Return a container value as Global and make sure the weak
   * callback is properly disposed of. All remove functionality should go
   * through this.
   */
    static Global<V> Release(PersistentContainerValue v)
    {
        Global<V> p;
        p.val_ = FromVal(v);
        if (Traits::kCallbackType != kNotWeak && p.IsWeak()) {
            Traits::DisposeCallbackData(
                p.template ClearWeak<typename Traits::WeakCallbackDataType>());
        }
        return p.Pass();
    }

    void RemoveWeak(const K& key)
    {
        Global<V> p;
        p.val_ = FromVal(Traits::Remove(&impl_, key));
        p.Reset();
    }

    void AnnotateStrongRetainer(Global<V>* persistent)
    {
        persistent->AnnotateStrongRetainer(label_);
    }

private:
    PersistentValueMapBase(PersistentValueMapBase&);
    void operator=(PersistentValueMapBase&);

    static bool SetReturnValueFromVal(ReturnValue<Value>* returnValue,
        PersistentContainerValue value)
    {
        bool hasValue = value != kPersistentContainerNotFound;
        if (hasValue) {
            returnValue->SetInternal(
                *reinterpret_cast<internal::Address*>(FromVal(value)));
        }
        return hasValue;
    }

    Isolate* isolate_;
    typename Traits::Impl impl_;
    const char* label_;
};

template <typename K, typename V, typename Traits>
inline void
PersistentValueMapBase<K, V, Traits>::RegisterExternallyReferencedObject(
    K& key)
{
    assert(Contains(key));
    V8::RegisterExternallyReferencedObject(
        reinterpret_cast<internal::Address*>(FromVal(Traits::Get(&impl_, key))),
        reinterpret_cast<internal::Isolate*>(GetIsolate()));
}

template <typename K, typename V, typename Traits>
class PersistentValueMap : public PersistentValueMapBase<K, V, Traits> {
public:
    explicit PersistentValueMap(Isolate* isolate)
        : PersistentValueMapBase<K, V, Traits>(isolate)
    {
    }
    PersistentValueMap(Isolate* isolate, const char* label)
        : PersistentValueMapBase<K, V, Traits>(isolate, label)
    {
    }

    typedef
        typename PersistentValueMapBase<K, V, Traits>::PersistentValueReference
            PersistentValueReference;

    /**
   * Put value into map. Depending on Traits::kIsWeak, the value will be held
   * by the map strongly or weakly.
   * Returns old value as Global.
   */
    Global<V> Set(const K& key, Local<V> value)
    {
        Global<V> persistent(this->isolate(), value);
        return SetUnique(key, &persistent);
    }

    /**
   * Put value into map, like Set(const K&, Local<V>).
   */
    Global<V> Set(const K& key, Global<V> value)
    {
        return SetUnique(key, &value);
    }

    /**
   * Put the value into the map, and set the 'weak' callback when demanded
   * by the Traits class.
   */
    Global<V> SetUnique(const K& key, Global<V>* persistent)
    {
        if (Traits::kCallbackType == kNotWeak) {
            this->AnnotateStrongRetainer(persistent);
        } else {
            WeakCallbackType callback_type = Traits::kCallbackType == kWeakWithInternalFields
                ? WeakCallbackType::kInternalFields
                : WeakCallbackType::kParameter;
            Local<V> value(Local<V>::New(this->isolate(), *persistent));
            persistent->template SetWeak<typename Traits::WeakCallbackDataType>(
                Traits::WeakCallbackParameter(this, key, value), WeakCallback,
                callback_type);
        }
        PersistentContainerValue old_value = Traits::Set(this->impl(), key, this->ClearAndLeak(persistent));
        return this->Release(old_value).Pass();
    }

    /**
   * Put a value into the map and update the reference.
   * Restrictions of GetReference apply here as well.
   */
    Global<V> Set(const K& key, Global<V> value,
        PersistentValueReference* reference)
    {
        *reference = this->Leak(&value);
        return SetUnique(key, &value);
    }

private:
    static void WeakCallback(
        const WeakCallbackInfo<typename Traits::WeakCallbackDataType>& data)
    {
        if (Traits::kCallbackType != kNotWeak) {
            PersistentValueMap<K, V, Traits>* persistentValueMap = Traits::MapFromWeakCallbackInfo(data);
            K key = Traits::KeyFromWeakCallbackInfo(data);
            Traits::Dispose(data.GetIsolate(),
                persistentValueMap->Remove(key).Pass(), key);
            Traits::DisposeCallbackData(data.GetParameter());
        }
    }
};

template <typename K, typename V, typename Traits>
class GlobalValueMap : public PersistentValueMapBase<K, V, Traits> {
public:
    explicit GlobalValueMap(Isolate* isolate)
        : PersistentValueMapBase<K, V, Traits>(isolate)
    {
    }
    GlobalValueMap(Isolate* isolate, const char* label)
        : PersistentValueMapBase<K, V, Traits>(isolate, label)
    {
    }

    typedef
        typename PersistentValueMapBase<K, V, Traits>::PersistentValueReference
            PersistentValueReference;

    /**
   * Put value into map. Depending on Traits::kIsWeak, the value will be held
   * by the map strongly or weakly.
   * Returns old value as Global.
   */
    Global<V> Set(const K& key, Local<V> value)
    {
        Global<V> persistent(this->isolate(), value);
        return SetUnique(key, &persistent);
    }

    /**
   * Put value into map, like Set(const K&, Local<V>).
   */
    Global<V> Set(const K& key, Global<V> value)
    {
        return SetUnique(key, &value);
    }

    /**
   * Put the value into the map, and set the 'weak' callback when demanded
   * by the Traits class.
   */
    Global<V> SetUnique(const K& key, Global<V>* persistent)
    {
        if (Traits::kCallbackType == kNotWeak) {
            this->AnnotateStrongRetainer(persistent);
        } else {
            WeakCallbackType callback_type = Traits::kCallbackType == kWeakWithInternalFields
                ? WeakCallbackType::kInternalFields
                : WeakCallbackType::kParameter;
            Local<V> value(Local<V>::New(this->isolate(), *persistent));
            persistent->template SetWeak<typename Traits::WeakCallbackDataType>(
                Traits::WeakCallbackParameter(this, key, value), OnWeakCallback,
                callback_type);
        }
        PersistentContainerValue old_value = Traits::Set(this->impl(), key, this->ClearAndLeak(persistent));
        return this->Release(old_value).Pass();
    }

    /**
   * Put a value into the map and update the reference.
   * Restrictions of GetReference apply here as well.
   */
    Global<V> Set(const K& key, Global<V> value,
        PersistentValueReference* reference)
    {
        *reference = this->Leak(&value);
        return SetUnique(key, &value);
    }

private:
    static void OnWeakCallback(
        const WeakCallbackInfo<typename Traits::WeakCallbackDataType>& data)
    {
        if (Traits::kCallbackType != kNotWeak) {
            auto map = Traits::MapFromWeakCallbackInfo(data);
            K key = Traits::KeyFromWeakCallbackInfo(data);
            map->RemoveWeak(key);
            Traits::OnWeakCallback(data);
            data.SetSecondPassCallback(SecondWeakCallback);
        }
    }

    static void SecondWeakCallback(
        const WeakCallbackInfo<typename Traits::WeakCallbackDataType>& data)
    {
        Traits::DisposeWeak(data);
    }
};

/**
 * A map that uses Global as value and std::map as the backing
 * implementation. Persistents are held non-weak.
 *
 * C++11 embedders don't need this class, as they can use
 * Global directly in std containers.
 */
template <typename K, typename V,
    typename Traits = DefaultPersistentValueMapTraits<K, V>>
class StdPersistentValueMap : public PersistentValueMap<K, V, Traits> {
public:
    explicit StdPersistentValueMap(Isolate* isolate)
        : PersistentValueMap<K, V, Traits>(isolate)
    {
    }
};

/**
 * A map that uses Global as value and std::map as the backing
 * implementation. Globals are held non-weak.
 *
 * C++11 embedders don't need this class, as they can use
 * Global directly in std containers.
 */
template <typename K, typename V,
    typename Traits = DefaultGlobalMapTraits<K, V>>
class StdGlobalValueMap : public GlobalValueMap<K, V, Traits> {
public:
    explicit StdGlobalValueMap(Isolate* isolate)
        : GlobalValueMap<K, V, Traits>(isolate)
    {
    }
};

class DefaultPersistentValueVectorTraits {
public:
    typedef std::vector<PersistentContainerValue> Impl;

    static void Append(Impl* impl, PersistentContainerValue value)
    {
        impl->push_back(value);
    }
    static bool IsEmpty(const Impl* impl)
    {
        return impl->empty();
    }
    static size_t Size(const Impl* impl)
    {
        return impl->size();
    }
    static PersistentContainerValue Get(const Impl* impl, size_t i)
    {
        return (i < impl->size()) ? impl->at(i) : kPersistentContainerNotFound;
    }
    static void ReserveCapacity(Impl* impl, size_t capacity)
    {
        impl->reserve(capacity);
    }
    static void Clear(Impl* impl)
    {
        impl->clear();
    }
};

/**
 * A vector wrapper that safely stores Global values.
 * C++11 embedders don't need this class, as they can use Global
 * directly in std containers.
 *
 * This class relies on a backing vector implementation, whose type and methods
 * are described by the Traits class. The backing map will handle values of type
 * PersistentContainerValue, with all conversion into and out of V8
 * handles being transparently handled by this class.
 */
template <typename V, typename Traits = DefaultPersistentValueVectorTraits>
class PersistentValueVector {
public:
    explicit PersistentValueVector(Isolate* isolate)
        : isolate_(isolate)
    {
    }

    ~PersistentValueVector()
    {
        Clear();
    }

    /**
   * Append a value to the vector.
   */
    void Append(Local<V> value)
    {
        Global<V> persistent(isolate_, value);
        Traits::Append(&impl_, ClearAndLeak(&persistent));
    }

    /**
   * Append a persistent's value to the vector.
   */
    void Append(Global<V> persistent)
    {
        Traits::Append(&impl_, ClearAndLeak(&persistent));
    }

    /**
   * Are there any values in the vector?
   */
    bool IsEmpty() const
    {
        return Traits::IsEmpty(&impl_);
    }

    /**
   * How many elements are in the vector?
   */
    size_t Size() const
    {
        return Traits::Size(&impl_);
    }

    /**
   * Retrieve the i-th value in the vector.
   */
    Local<V> Get(size_t index) const
    {
        return Local<V>::New(isolate_, FromVal(Traits::Get(&impl_, index)));
    }

    /**
   * Remove all elements from the vector.
   */
    void Clear()
    {
        size_t length = Traits::Size(&impl_);
        for (size_t i = 0; i < length; i++) {
            Global<V> p;
            p.val_ = FromVal(Traits::Get(&impl_, i));
        }
        Traits::Clear(&impl_);
    }

    /**
   * Reserve capacity in the vector.
   * (Efficiency gains depend on the backing implementation.)
   */
    void ReserveCapacity(size_t capacity)
    {
        Traits::ReserveCapacity(&impl_, capacity);
    }

private:
    static PersistentContainerValue ClearAndLeak(Global<V>* persistent)
    {
        V* v = persistent->val_;
        persistent->val_ = nullptr;
        return reinterpret_cast<PersistentContainerValue>(v);
    }

    static V* FromVal(PersistentContainerValue v)
    {
        return reinterpret_cast<V*>(v);
    }

    Isolate* isolate_;
    typename Traits::Impl impl_;
};

} // namespace v8

#endif // V8_UTIL_H
